// Copyright (C) 2007 AbstraX (Data Extraction Magazine)
// http://www.AbstraX.com, support@AbstraX.com
//
// This code is provided AS IS, with NO WARRANTY expressed or implied.
// Any use of this free open source code is at your own risk.
//
// You are hereby granted the right to use it and change it,
// provided that you acknowledge AbstraX somewhere in your
// source files, documentation, web site and application.

using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Reflection;
using AbstraX.ImageEditor.localhost;
using System.Diagnostics;

namespace AbstraX.ImageEditor
{
    [ProgId("AbstraX.ImageEditorControl")]
    [ClassInterface(ClassInterfaceType.AutoDual), ComSourceInterfaces(typeof(ControlEvents))]
    [Guid("5A1B3121-9413-455E-BA85-312648223E9D")]
    [ComVisible(true)]
    public partial class ImageEditorControl : UserControl
    {
        [DllImport("user32.dll")]
        private static extern int RegisterHotKey(IntPtr hWnd, int id, KeyModifiers modifier, Keys vk);

        [DllImport("user32.dll")]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        const int HOTKEY_ID = 31197;    //any number to be used as an id within this app
        const int WM_HOTKEY = 0x0312;

        public event ControlEventHandler OnClose;
        private ImageService service;
        private bool cancelledOrUploaded;

		public enum KeyModifiers        //enum to call 3rd parameter of RegisterHotKey easily
		{
			None = 0,
			Alt = 1,
			Control = 2,
			Shift = 4,
			Windows = 8
		}

        public string Key { get; set; }
        public string FileName { get; set; }
        public object BrowserWindow { get; set; }

        public ImageEditorControl()
        {
            InitializeComponent();
        }

        ///	<summary>
        ///	Register the class as a	control	and	set	it's CodeBase entry
        ///	</summary>
        ///	<param name="key">The registry key of the control</param>
        [ComRegisterFunction()]
        public static void RegisterClass(string key)
        {
            // Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it
            StringBuilder sb = new StringBuilder(key);

            sb.Replace(@"HKEY_CLASSES_ROOT\", "");
            // Open the CLSID\{guid} key for write access
            RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

            // And create	the	'Control' key -	this allows	it to show up in
            // the ActiveX control container
            RegistryKey ctrl = k.CreateSubKey("Control");
            ctrl.Close();

            // Next create the CodeBase entry	- needed if	not	string named and GACced.
            RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);
            inprocServer32.SetValue("CodeBase", Assembly.GetExecutingAssembly().CodeBase);
            inprocServer32.Close();
            // Finally close the main	key
            k.Close();

            Debug.WriteLine("ImageEditorControl registered successfully.");
        }

        ///	<summary>
        ///	Called to unregister the control
        ///	</summary>
        ///	<param name="key">Tke registry key</param>
        [ComUnregisterFunction()]
        public static void UnregisterClass(string key)
        {
            StringBuilder sb = new StringBuilder(key);
            sb.Replace(@"HKEY_CLASSES_ROOT\", "");

            // Open	HKCR\CLSID\{guid} for write	access
            RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

            // Delete the 'Control'	key, but don't throw an	exception if it	does not exist
            k.DeleteSubKey("Control", false);

            // Next	open up	InprocServer32
            //RegistryKey	inprocServer32 = 
            k.OpenSubKey("InprocServer32", true);

            // And delete the CodeBase key,	again not throwing if missing
            k.DeleteSubKey("CodeBase", false);

            // Finally close the main key
            k.Close();

            Debug.WriteLine("ImageEditorControl unregistered successfully.");
        }

        private void ImageEditorControl_Load(object sender, EventArgs e)
        {
            Show();
            Update();
            propGrid.SelectedObject = imgThumb.Thumbnail = imgMain.Image;

            if (this.ParentForm != null)
            {
                this.ParentForm.KeyPreview = true;
                this.ParentForm.KeyDown += new KeyEventHandler(ImageEditorControl_KeyDown);
            }

            if (this.Parent != null)
            {
                this.Parent.KeyDown += new KeyEventHandler(ImageEditorControl_KeyDown);
            }

            var check = RegisterHotKey(Handle, HOTKEY_ID, KeyModifiers.Control, Keys.V);

            this.Focus();
        }

        protected override void OnHandleDestroyed(EventArgs e)
        {
            UnregisterHotKey(Handle, HOTKEY_ID);

            if (!cancelledOrUploaded)
            {
                service.CancelUpload(this.Key);
            }

            base.OnHandleDestroyed(e);
        }

        // Refresh property grid and thumbnail contro, when image changes
        private void ImageControl_OnChange(object sender, EventArgs e)
        {
            propGrid.SelectedObject = imgThumb.Thumbnail = imgMain.Image;
        }

        // Generic method for all menu commands
        private void Command_Click(object sender, EventArgs e)
        {
            if (sender == mnuFileOpen)              imgMain.Open();
            else if (sender == mnuFileSave)         imgMain.Save();
            else if (sender == mnuFileSaveAs)       imgMain.SaveAs();
        }

        private void mnuFile_DropDownOpening(object sender, EventArgs e)
        {
            mnuFileSaveAs.Enabled = (imgMain.Image.FileName != null);
            mnuFileSave.Enabled = (mnuFileSaveAs.Enabled && imgMain.Image.IsChanged);
        }

        private void OnSave(object sender, EventArgs e)
        {
            var bytes = imgMain.GetImageData(this.FileName);

            service.UploadTreeNodeImage(this.Key, this.FileName, bytes);

            cancelledOrUploaded = true;

            OnClose();
        }

        private void OnCancel(object sender, EventArgs e)
        {
            cancelledOrUploaded = true;

            service.CancelUpload(this.Key);

            OnClose();
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_HOTKEY)
            {
                imgMain.PasteImage();
            }

            base.WndProc(ref m);
        }

        public void PasteImage()
        {
            imgMain.PasteImage();
        }

        private void ImageEditorControl_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.V && e.Modifiers == Keys.Control)
            {
                imgMain.PasteImage();
            }
        }

        private void imgMain_Load(object sender, EventArgs e)
        {
            service = new ImageService();

            PingTimer.Tick += new EventHandler(PingTimerTick);
            PingTimer.Start();
        }

        private void PingTimerTick(object sender, EventArgs e)
        {
            service.Ping(this.Key);
        }
    }

    /// <summary>
    /// Event handler for events that will be visible from JavaScript
    /// </summary>
    public delegate void ControlEventHandler();

    /// <summary>
    /// This interface shows events to javascript
    /// </summary>
    [Guid("68BD4E0D-D7BC-4cf6-BEB7-CAB950161E79")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface ControlEvents
    {
        //Add a DispIdAttribute to any members in the source interface to specify the COM DispId.
        [DispId(0x60020001)]
        void OnClose(); //This method will be visible from JS
    }
}